#include "dynamicxucs.h"
#include "dm/dmdef.h"

#include <algorithm>


DynamicXUCS::DynamicXUCS(const DynamicX &type, const double &maxThreshold, QWidget *parent)
    : AWidget{parent}
    , m_type{type}
    , m_maxThreshold{maxThreshold}
{
    setFixedSize(330, 330);

    m_threshold = DYNAMIC_X_THRESHOLD_DEFAULT;

    m_ratio = DYNAMIC_X_RATIO_DEFAULT;

    setMouseTracking(true);
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

}

void DynamicXUCS::setThreshold(const double &threshold)
{
    if (m_threshold != threshold) {
        m_threshold = std::clamp(threshold, DYNAMIC_X_THRESHOLD_MIN, m_maxThreshold);
        update();
    }
}

void DynamicXUCS::setRatio(const uint8_t &ratio)
{
    if (m_type == DynamicX::Compressor && m_ratio != ratio) {
        m_ratio = std::clamp(ratio, DYNAMIC_X_RATIO_MIN, DYNAMIC_X_RATIO_MAX);
        update();
    }
}

void DynamicXUCS::paintEvent(QPaintEvent *event)
{
    QWidget::paintEvent(event);

    const double labelWidth = 30 * ratio();

    m_rectThreshold = QRectF(0, 0, 15 * ratio(), 15 * ratio());
    m_rectRatio = QRectF(0, 0, 15 * ratio(), 15 * ratio());

    QStyleOption opt;
    opt.initFrom(this);
    QPainter painter(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setRenderHint(QPainter::SmoothPixmapTransform, true);

    auto pen = painter.pen();
    pen.setWidth(1);
    pen.setColor(Qt::gray);
    painter.setPen(pen);

    // 坐标系实际大小
    const auto aw = width() - 1.5 * labelWidth;
    const auto ah = height() - 1.5 * labelWidth;

    m_cRect = QRectF(labelWidth, labelWidth / 2, aw, ah);

    const auto ox = m_cRect.bottomLeft().x();
    const auto oy = m_cRect.bottomLeft().y();

    // 网格
    for (int gain = DYNAMIC_X_THRESHOLD_MIN; gain <= m_maxThreshold; gain += 12) {
        auto x = ox + aw * (gain - DYNAMIC_X_THRESHOLD_MIN) / (m_maxThreshold - DYNAMIC_X_THRESHOLD_MIN);
        auto y = oy - ah * (gain - DYNAMIC_X_THRESHOLD_MIN) / (m_maxThreshold - DYNAMIC_X_THRESHOLD_MIN);

        painter.drawLine(QPointF(x, m_cRect.topLeft().y()), QPointF(x, oy));
        painter.drawLine(QPointF(ox, y), QPointF(m_cRect.bottomRight().x(), y));

        painter.drawText(QRectF(ox - labelWidth, y - labelWidth / 2, labelWidth - 5 * ratio(), labelWidth), Qt::AlignVCenter | Qt::AlignRight, QString::number(gain));
        painter.drawText(QRectF(x - labelWidth / 2, oy + 5 * ratio(), labelWidth, labelWidth), Qt::AlignHCenter, QString::number(gain));
    }

    m_rectThreshold.moveCenter(QPointF(ox + aw * (m_threshold - DYNAMIC_X_THRESHOLD_MIN) / (m_maxThreshold - DYNAMIC_X_THRESHOLD_MIN),
                                       oy - ah * (m_threshold - DYNAMIC_X_THRESHOLD_MIN) / (m_maxThreshold - DYNAMIC_X_THRESHOLD_MIN)));

    painter.setPen(Qt::NoPen);
    painter.setBrush(QColor(119, 192, 51, 48));

    QPointF points[4];
    points[0] = m_cRect.bottomLeft();
    points[1] = m_rectThreshold.center();
    switch (m_type) {
    case DynamicX::Limiter:
        points[2] = QPointF(m_cRect.bottomRight().x(), m_rectThreshold.center().y());
        break;
    case DynamicX::Compressor:
        m_rectRatio.moveCenter(QPointF(m_cRect.bottomRight().x(), m_rectThreshold.center().y() - (m_rectThreshold.center().y() - m_cRect.topRight().y()) / m_ratio));
        points[2] = m_rectRatio.center();
        break;
    default:
        break;
    }
    points[3] = m_cRect.bottomRight();

    painter.drawConvexPolygon(points, 4);

    pen.setStyle(Qt::DashLine);
    pen.setColor(QColor(119, 192, 51));
    painter.setPen(pen);
    painter.drawLine(QPointF(m_cRect.topLeft().x(), points[1].y()), QPointF(m_cRect.topRight().x(), points[1].y()));
    painter.drawLine(QPointF(points[1].x(), m_cRect.topLeft().y()), QPointF(points[1].x(), m_cRect.bottomLeft().y()));
    if (DynamicX::Compressor == m_type) {
        pen.setStyle(Qt::DashLine);
        painter.setPen(pen);
        painter.drawLine(QPointF(m_cRect.topLeft().x(), points[2].y()), QPointF(m_cRect.topRight().x(), points[2].y()));
    }

    pen.setWidth(2);
    pen.setStyle(Qt::SolidLine);
    painter.setPen(pen);
    painter.drawLine(points[0], points[1]);
    painter.drawLine(points[1], points[2]);

    painter.setPen(Qt::white);
    painter.setBrush(Qt::white);

    painter.drawEllipse(m_rectThreshold);
    if (DynamicX::Compressor == m_type) {
        painter.drawEllipse(m_rectRatio);
    }
}

void DynamicXUCS::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        if (m_rectThreshold.contains(event->pos())) {
            m_selected = ThresholdSelected;
        }
        else if (m_type == DynamicX::Compressor && m_rectRatio.contains(event->pos())) {
            m_selected = RatioSelected;
        }
        else {
            m_selected = NotSelected;
        }
    }

    QWidget::mousePressEvent(event);
}

void DynamicXUCS::mouseMoveEvent(QMouseEvent *event)
{
    if (m_rectThreshold.contains(event->pos())) {
        setCursor(Qt::SizeBDiagCursor);
    }
    else if (m_type == DynamicX::Compressor && m_rectRatio.contains(event->pos())) {
        setCursor(Qt::SizeVerCursor);
    }
    else if (NotSelected == m_selected) {
        setCursor(Qt::ArrowCursor);
    }
    if (ThresholdSelected == m_selected) {
        emit thresholdChanged(toThreshold(event->pos()));
    }
    else if (RatioSelected == m_selected) {
        emit ratioChanged(toRatio(event->pos()));
    }
}

void DynamicXUCS::mouseReleaseEvent(QMouseEvent *event)
{
    if (NotSelected != m_selected && event->button() == Qt::LeftButton) {
        m_selected = NotSelected;
    }

    QWidget::mouseReleaseEvent(event);
}

double DynamicXUCS::toThreshold(const QPointF &point) const
{
    return m_maxThreshold - (m_maxThreshold - DYNAMIC_X_THRESHOLD_MIN) * (point.y() - m_cRect.topLeft().y()) / (m_cRect.bottomLeft().y() - m_cRect.topLeft().y());
}

uint8_t DynamicXUCS::toRatio(const QPointF &point) const
{
    if (point.y() >= m_rectThreshold.center().y()) {
        return DYNAMIC_X_RATIO_MAX;
    }
    if (point.y() <= m_cRect.y()) {
        return DYNAMIC_X_RATIO_MIN;
    }
    return (m_maxThreshold - m_threshold) / ((m_maxThreshold - DYNAMIC_X_THRESHOLD_MIN) * (m_cRect.bottomRight().y() - point.y()) / m_cRect.height() + DYNAMIC_X_THRESHOLD_MIN - m_threshold);
}
